package org.wenhua.yuan.and.module_flutter.flutterM1.bridge

import android.content.Context
import android.os.Looper
import android.util.Log
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.embedding.engine.FlutterJNI
import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.embedding.engine.loader.FlutterLoader
import org.wenhua.yuan.and.module_flutter.flutterM1.bridge.JFlutterLoader.Companion.FIX_SO
import org.wenhua.yuan.and.module_flutter.util.JExecutor
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream

/**
 * preLoadFlutterEngine：Flutter提升加载速度，实现快速打开Flutter模块
 * 实现-多个Flutter实例引擎，并分别加载进入不同的dart入口文件
 * preLoadFlutterEngine和preLoadDartVM，两者都是优化提升flutter模块打开速度的。
 * 对比而看，preLoadFlutterEngine更加明显。
 */
class JFlutterCacheManager private constructor() {
    companion object {
        const val MODULE_NAME_FINANCE = "finance"
        const val MODULE_NAME_DEFAULT = "main"

        @JvmStatic
        @get:Synchronized
        var instance: JFlutterCacheManager? = null
            get() {
                if (field == null) {
                    field = JFlutterCacheManager()
                }
                return field
            }
            private set
    }

    /**【优化1】FlutterEngine预加载 - JBaseApplication中方法onCreate使用*/
    fun preLoadFlutterEngine (context: Context) {
        val msgQ = Looper.myQueue()
        // 热更新方案中：在Flutter引擎预加载之前，要完成so的拷贝任务。
        copyAssetsFile2FilesDir(context, FIX_SO, listener = {
            // 仅在主线程空闲时间，启用异步线程执行预加载任务
            // 在这里预加载执行2个FlutterEngine（同时会在flutter侧，开启两个flutter入口）
            msgQ.addIdleHandler {
                initFlutterEngine(context, MODULE_NAME_FINANCE)
                initFlutterEngine(context, MODULE_NAME_DEFAULT)
                false
            }
        })

    }
    /**【优化2】DartVM预加载:初始化和加载apk资源 - JBaseApplication中方法onCreate使用*/
    fun preLoadDartVM (context: Context) {
        val setting = FlutterLoader.Settings()
        var flutterLoader = FlutterLoader()
        flutterLoader.startInitialization(context, setting)
        val mainHandler:android.os.Handler = android.os.Handler(Looper.myLooper()!!)
        flutterLoader.ensureInitializationCompleteAsync(context, arrayOf(), mainHandler){
            Log.i("JFlutterCacheManager", "Flutter preLoadDartVM is done")
        }
    }


    fun getFlutterEngineCached (moduleName: String, context: Context?) : FlutterEngine {
        var engine = FlutterEngineCache.getInstance()[moduleName]
        if (engine == null && context != null) {
            // 当获取FlutterEngine为空时，此时执行加载初始化 FlutterEngine 实例对象
            engine = initFlutterEngine(context, moduleName)
        }
        return engine!!
    }

    /** 获取当前FlutterEngine缓存中是否已存在moduleName*/
    fun hasCached (moduleName:String):Boolean {
        return FlutterEngineCache.getInstance().contains(moduleName)
    }
    fun clearCache (moduleName: String) {
        val engine:FlutterEngine? = FlutterEngineCache.getInstance()[moduleName]
        engine?.destroy()
        FlutterEngineCache.getInstance().remove(moduleName)
    }

    /**
     * 初始化FlutterEngine
     * （JFlutterLoader.get()）使热更新方案实现在创建flutterEngine过程中，
     * 但，若要实现。还需将要做更新的so文件拷贝到JFlutterLoader所指定的路径目录下。当该目录so文件非空时，则会自动加载。
     *
     * 【如当下实现逻辑】：首先打包一个新的release-apk，然后解压提取出此时的libapp.so文件（修改名称为libappfix.so），放到目录assets/下。
     * 之后，当执行代码时，会通过copyAssetsFile2FilesDir将assets/目录下的so包拷贝到新指定的将会加载的目录，接下来顺理成章完成热更新。
     * */
    private fun initFlutterEngine(context: Context, moduleName: String): FlutterEngine? {
        var flutterEngine:FlutterEngine = FlutterEngine(context, JFlutterLoader.get(), FlutterJNI())
        // flutter引擎创建完成后，注册通信插件
        FlutterBridgeCompl.init(flutterEngine)
        // 根据给定的DartEntrypoint开始执行Dart代码
        //  Flutter 1.24 FlutterMain被废弃了，用FlutterMain.findAppBundlePath()换成(FlutterLoader().findAppBundlePath()==)JFlutterLoader.get()
//        flutterEngine.dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint(FlutterMain.findAppBundlePath(), moduleName))
        //  fix：java.lang.NullPointerException: Attempt to read from field 'java.lang.String io.flutter.embedding.engine.loader.FlutterApplicationInfo.flutterAssetsDir' on a null object reference
        flutterEngine.dartExecutor
            .executeDartEntrypoint(DartExecutor.DartEntrypoint(JFlutterLoader.get().findAppBundlePath(), moduleName))
        FlutterEngineCache.getInstance().put(moduleName, flutterEngine) // 缓存起来
        return flutterEngine
    }

    /**
     * 热更新方案中：在Flutter引擎预加载之前，要完成so的拷贝任务。
     * 将文件从assets目录中的文件复制到/data/data/包名/files/ 目录中
     * @param fileName 要copy的文件名，如xx.xx
     */
    private fun copyAssetsFile2FilesDir(context: Context, fileName: String, listener: (() -> Unit)?) {
        JExecutor.execute(runnable = Runnable {
            doCopy(context, fileName)
            listener?.invoke()
        })
    }

    private fun doCopy(context: Context, fileName: String) {
        var inputStream: InputStream? = null
        var fos: FileOutputStream? = null
        try {
            val file = File(context.filesDir.absolutePath + File.separator.toString() + fileName)
            if (file.exists()) return
            inputStream = context.assets.open(fileName)
            fos = FileOutputStream(file)
            var len = -1
            val buffer = ByteArray(1024)
            while (inputStream.read(buffer).also { len = it } != -1) {
                fos.write(buffer, 0, len)
            }
            fos.flush() //刷新缓存区

        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            inputStream?.close()
            fos?.close()
        }
    }


}